Load Packages

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.3     ✓ purrr   0.3.4
## ✓ tibble  3.1.2     ✓ dplyr   1.0.6
## ✓ tidyr   1.1.3     ✓ stringr 1.4.0
## ✓ readr   1.4.0     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(moderndive)
library(broom)
library(infer)
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
## 
##     lift
library(moments)
library(HistData)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
library(plotly)
## 
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
library(scales)
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
library(utils)
library(stringr)

Load Data

master <- data.table::fread("2020-2021.csv")

App Pitching

AppPitching <- master %>% 
  filter(PitcherTeam == "APP_MOU" | PitcherTeam == "APP_PRA")

Pitch Called

AppPitching <- AppPitching %>% 
  filter(PitchCall == "StrikeCalled" | PitchCall == "BallCalled")

Create Frames

AppPitching <- AppPitching %>% 
  mutate(Frame = case_when(
    PlateLocSide > .5417 | PlateLocSide < -.5417 & PitchCall == "StrikeCalled" ~ "Framed",
    PlateLocHeight > 3.243 | PlateLocHeight < 1.9117 & PitchCall == "StrikeCalled" ~ "Framed",
    PlateLocSide < .5417 & PlateLocSide > -.5417 & PlateLocHeight < 3.243 & PlateLocHeight > 1.9117 ~ "Strike",
    TRUE ~ "Ball"
  ))

AppPitching <- AppPitching %>% 
  select(Date, PitcherTeam, PitcherThrows, PitcherSet, BatterSide, Balls, Strikes, PitchCall, TaggedPitchType, Catcher, CatcherStance, Frame, PlateLocHeight, PlateLocSide) %>% 
  na.omit()

Catcher Tables

Cross <- AppPitching %>% 
  filter(Catcher == "Cross, Hayden")

Lipson <- AppPitching %>% 
  filter(Catcher == "Lipson, Jack")

Arnold <- AppPitching %>% 
  filter(Catcher == "Arnold, Carson")

Lewis <- AppPitching %>% 
  filter(Catcher == "Lewis, Trent")

Yakubinis <- AppPitching %>% 
  filter(Catcher == "Yakubinis, JD")

Splits

set.seed(500)
rows <- sample(nrow(AppPitching))
App <- AppPitching[rows, ]
split <- round(nrow(App)*0.70)
train <- App[1:split, ]
test <- App[(split + 1):nrow(App), ]

My Control

myControl <- trainControl(
  method = "cv", 
  number = 5)

Random Forest

rforest <- train(
  PitchCall ~ . - Date - PitcherTeam,
  tuneLength = 5,
  data = train, 
  method = "ranger",
  trControl = myControl)
rforest$finalModel
## Ranger result
## 
## Call:
##  ranger::ranger(dependent.variable.name = ".outcome", data = x,      mtry = min(param$mtry, ncol(x)), min.node.size = param$min.node.size,      splitrule = as.character(param$splitrule), write.forest = TRUE,      probability = classProbs, ...) 
## 
## Type:                             Classification 
## Number of trees:                  500 
## Sample size:                      5345 
## Number of independent variables:  212 
## Mtry:                             159 
## Target node size:                 1 
## Variable importance mode:         none 
## Splitrule:                        gini 
## OOB prediction error:             7.58 %

GLMnet

GLMnetmod <- train(
  PitchCall ~ . - Date - PitcherTeam,
  data = train, 
  method = "glm",
  trControl = myControl)
## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from a rank-deficient fit may be misleading

## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from a rank-deficient fit may be misleading

## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from a rank-deficient fit may be misleading

## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from a rank-deficient fit may be misleading

## Warning in predict.lm(object, newdata, se.fit, scale = 1, type = if (type == :
## prediction from a rank-deficient fit may be misleading
print(GLMnetmod)
## Generalized Linear Model 
## 
## 5345 samples
##   13 predictor
##    2 classes: 'BallCalled', 'StrikeCalled' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 4276, 4276, 4276, 4276, 4276 
## Resampling results:
## 
##   Accuracy   Kappa    
##   0.8297474  0.5484838

Which Model is best?

ANS <- resamples(list(RF = rforest, GLM = GLMnetmod))
dotplot(ANS, scales = "free", layout = c(1, 3))

bwplot(ANS, layout = c(1, 3), scales = "free", as.table = TRUE)

Proportion of Framed Strikes

Cross2 <- Cross %>% 
  filter(Frame == "Framed" | Frame == "Ball")
(Cross3 <- Cross2 %>% 
  summarize(FramedPitches = sum(Frame == "Framed"),
            prop          = FramedPitches/nrow(Cross2)))
##   FramedPitches      prop
## 1           697 0.7005025
Arnold2 <- Arnold %>% 
  filter(Frame == "Framed" | Frame == "Ball")
(Arnold3 <- Arnold2 %>% 
  summarize(FramedPitches = sum(Frame == "Framed"),
            prop          = FramedPitches/nrow(Arnold2)))
##   FramedPitches     prop
## 1           447 0.738843
Lipson2 <- Lipson %>% 
  filter(Frame == "Framed" | Frame == "Ball")
(Lipson3 <- Lipson2 %>% 
  summarize(FramedPitches = sum(Frame == "Framed"),
            prop          = FramedPitches/nrow(Lipson2)))
##   FramedPitches      prop
## 1           158 0.6899563
Lewis2 <- Lewis %>% 
  filter(Frame == "Framed" | Frame == "Ball")
(Lewis3 <- Lewis2 %>% 
  summarize(FramedPitches = sum(Frame == "Framed"),
            prop          = FramedPitches/nrow(Lewis2)))
##   FramedPitches      prop
## 1            43 0.7413793
Yak2 <- Yakubinis %>% 
  filter(Frame == "Framed" | Frame == "Ball")
(Yak3 <- Yak2 %>% 
  summarize(FramedPitches = sum(Frame == "Framed"),
            prop          = FramedPitches/nrow(Yak2)))
##   FramedPitches      prop
## 1            52 0.6753247

Bootstraps

Over 50 seasons, how many strikes are called per catcher?

CrossBoot <- Cross %>% 
  specify(response = PitchCall, success = "StrikeCalled") %>% 
  generate(reps = 50, type = "bootstrap") %>% 
  calculate(stat = "prop")

ArnoldBoot <- Arnold %>% 
  specify(response = PitchCall, success = "StrikeCalled") %>% 
  generate(reps = 50, type = "bootstrap") %>% 
  calculate(stat = "prop")

LipsonBoot <- Lipson %>% 
  specify(response = PitchCall, success = "StrikeCalled") %>% 
  generate(reps = 50, type = "bootstrap") %>% 
  calculate(stat = "prop")

LewisBoot <- Lewis %>% 
  specify(response = PitchCall, success = "StrikeCalled") %>% 
  generate(reps = 50, type = "bootstrap") %>% 
  calculate(stat = "prop")

YakBoot <- Yakubinis %>% 
  specify(response = PitchCall, success = "StrikeCalled") %>% 
  generate(reps = 50, type = "bootstrap") %>% 
  calculate(stat = "prop")

Bootstrap Comp

percentile_ci_Cross <- CrossBoot %>% 
  get_confidence_interval(level = .95, type = "percentile")
percentile_ci_Arnold <- ArnoldBoot %>% 
  get_confidence_interval(level = .95, type = "percentile")
percentile_ci_Lipson <- LipsonBoot %>% 
  get_confidence_interval(level = .95, type = "percentile")
percentile_ci_Lewis <- LewisBoot %>% 
  get_confidence_interval(level = .95, type = "percentile")
percentile_ci_Yak <- YakBoot %>% 
  get_confidence_interval(level = .95, type = "percentile")

visualize(CrossBoot, dens_color = "BLACK") +
  shade_confidence_interval(endpoints = percentile_ci_Cross, color = "gold", fill = "gold") +
  labs(title = "Cross' Bootstrap Distribution for Proportion of Framed Pitches",
       x = "Proportion of Framed Pitches",
       y = "Count",
       caption = "Estimated for 50 seasons with 95% confidence") +
  theme_linedraw()

visualize(ArnoldBoot, dens_color = "BLACK") +
  shade_confidence_interval(endpoints = percentile_ci_Arnold, color = "gold", fill = "gold") +
  labs(title = "Arnold's Bootstrap Distribution for Proportion of Framed Pitches",
       x = "Proportion of Framed Pitches",
       y = "Count",
       caption = "Estimated for 50 seasons with 95% confidence") +
  theme_linedraw()

visualize(LipsonBoot, dens_color = "BLACK") +
  shade_confidence_interval(endpoints = percentile_ci_Lipson, color = "gold", fill = "gold") +
  labs(title = "Lipson's Bootstrap Distribution for Proportion of Framed Pitches",
       x = "Proportion of Framed Pitches",
       y = "Count",
       caption = "Estimated for 50 seasons with 95% confidence") +
  theme_linedraw()

visualize(LewisBoot, dens_color = "BLACK") +
  shade_confidence_interval(endpoints = percentile_ci_Lewis, color = "gold", fill = "gold") +
  labs(title = "Lewis' Bootstrap Distribution for Proportion of Framed Pitches",
       x = "Proportion of Framed Pitches",
       y = "Count",
       caption = "Estimated for 50 seasons with 95% confidence") +
  theme_linedraw()

visualize(YakBoot, dens_color = "BLACK") +
  shade_confidence_interval(endpoints = percentile_ci_Yak, color = "gold", fill = "gold") +
  labs(title = "Yakubinis' Bootstrap Distribution for Proportion of Framed Pitches",
       x = "Proportion of Framed Pitches",
       y = "Count",
       caption = "Estimated for 50 seasons with 95% confidence") +
  theme_linedraw()

(CrossSummaries <- CrossBoot %>% 
  summarize(skew = skewness(stat),
            kurt = kurtosis(stat)))
## # A tibble: 1 x 2
##     skew  kurt
##    <dbl> <dbl>
## 1 -0.250  2.56
(ArnoldSummaries <- ArnoldBoot %>% 
  summarize(skew = skewness(stat),
            kurt = kurtosis(stat)))
## # A tibble: 1 x 2
##    skew  kurt
##   <dbl> <dbl>
## 1 0.265  2.90
(LipsonSummaries <- LipsonBoot %>% 
  summarize(skew = skewness(stat),
            kurt = kurtosis(stat)))
## # A tibble: 1 x 2
##    skew  kurt
##   <dbl> <dbl>
## 1 0.175  2.23
(LewisSummaries <- LewisBoot %>% 
  summarize(skew = skewness(stat),
            kurt = kurtosis(stat)))
## # A tibble: 1 x 2
##    skew  kurt
##   <dbl> <dbl>
## 1 0.191  2.81
(YakSummaries <- YakBoot %>% 
  summarize(skew = skewness(stat),
            kurt = kurtosis(stat)))
## # A tibble: 1 x 2
##     skew  kurt
##    <dbl> <dbl>
## 1 -0.384  3.33

All graphs are approximately symmetric meaning they are valid (skew between -.5 and .5). Lipson is most symmetric and has least outliers as kurt is low. Cross also doesn’t have many outliers, and Arnold has more outliers than Cross.

x <- c(-.95,.95,.95,-.95,-.95)
y <- c(1.6,1.6,3.5,3.5,1.6)
sz <- data_frame(x, y)
## Warning: `data_frame()` was deprecated in tibble 1.1.0.
## Please use `tibble()` instead.
x1 <- c(-.95, -.95, 0, .95, .95, -.95)
y1 <- c(0, .15, .25, .15, 0, 0)
hp <- data_frame(x1, y1)

x2 <- c(1.61, 3.11, 3.11, 1.61, 1.61)
y2 <- c(0, 0, 5.5, 5.5, 0)
rhb <- data_frame(x2, y2)

x3 <- c(-1.61, -3.11, -3.11, -1.61, -1.61)
y3 <- c(0, 0, 5.5, 5.5, 0)
lhb <- data_frame(x3, y3)

x4 <- c(-.95, .95, .95, -.95)
y4 <- c(2.233, 2.233, 2.866, 2.866)
szv <- data_frame(x4, y4)

x5 <- c(-0.316, -0.316, 0.316, 0.316)
y5 <- c(1.6, 3.5, 3.5, 1.6)
szh <- data_frame(x5, y5)

x6 <- c(0, 0, 1.267, 1.267, .95, 1.267, 1.267, 0, 0, 0, -1.267, -1.267, -.95, -1.267, -1.267, 0)
y6 <- c(3.5, 3.817, 3.817, 2.55, 2.55, 2.55, 1.283, 1.283, 1.6, 1.283, 1.283, 2.55, 2.55, 2.55, 3.817, 3.817)
bllz <- data_frame(x6, y6)

Interactive graphs

CrossBallsFramedL <- Cross %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Left")
CrossBallsFramedR <- Cross %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Right")

ArnoldBallsFramedL <- Arnold %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Left")
ArnoldBallsFramedR <- Arnold %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Right")

LipsonBallsFramedL <- Lipson %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Left")
LipsonBallsFramedR <- Lipson %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Right")

LewisBallsFramedL <- Lewis %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Left")
LewisBallsFramedR <- Lewis %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Right")

YakBallsFramedL <- Yakubinis %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Left")
YakBallsFramedR <- Yakubinis %>% 
  filter(PlateLocHeight > 3.410 | PlateLocHeight < 1.745 |
         PlateLocSide > .70833 | PlateLocSide < -.700833,
         PitchCall == "StrikeCalled",
         PitcherThrows == "Right")

Point Graphics

CrossGraphL <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Cross' Stolen Pitches For Left Handed Pitchers") +
  geom_point(data = CrossBallsFramedL, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(CrossGraphL, tootip = "text"), dragmode = "pan")
CrossGraphR <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Cross' Stolen Pitches For Right Handed Pitchers") +
  geom_point(data = CrossBallsFramedR, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(CrossGraphR, tootip = "text"), dragmode = "pan")
ArnoldGraphL <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Arnold's Stolen Pitches For Left Handed Pitchers") +
  geom_point(data = ArnoldBallsFramedL, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(ArnoldGraphL, tootip = "text"), dragmode = "pan")
ArnoldGraphR <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Arnold's Stolen Pitches For Right Handed Pitchers") +
  geom_point(data = ArnoldBallsFramedR, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(ArnoldGraphR, tootip = "text"), dragmode = "pan")
LipsonGraphL <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Lipson's Stolen Pitches For Left Handed Pitchers") +
  geom_point(data = LipsonBallsFramedL, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(LipsonGraphL, tootip = "text"), dragmode = "pan")
LipsonGraphR <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Lipson's Stolen Pitches For Right Handed Pitchers") +
  geom_point(data = LipsonBallsFramedR, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(LipsonGraphR, tootip = "text"), dragmode = "pan")
# Lewis did not steal any strikes for lefties

LewisGraphR <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Lewis' Stolen Pitches For Right Handed Pitchers") +
  geom_point(data = LewisBallsFramedR, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(LewisGraphR, tootip = "text"), dragmode = "pan")
YakGraphL <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Yakubinis' Stolen Pitches For Left Handed Pitchers") +
  geom_point(data = YakBallsFramedL, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(YakGraphL, tootip = "text"), dragmode = "pan")
YakGraphR <- ggplot() +
  geom_path(data = sz, aes(x = x, y = y)) +
  geom_polygon(hp, mapping = aes(x = x1, y = y1), color = "black", fill = "white") +
  geom_path(data = rhb, aes(x = x2, y = y2), color = "lightgray") +
  geom_path(data = lhb, aes(x = x3, y = y3), color = "lightgray") +
  geom_text(mapping = aes(x = 2.36, y = 2.75), label = paste("RHB"), color = "lightgray") +
  geom_text(mapping = aes(x = -2.36, y = 2.75), label = paste("LHB"), color = "lightgray") +
  coord_equal() +
  xlab("Plate Width") +
  ylab("Plate Height") +
  ggtitle("Yakubinis' Stolen Pitches For Right Handed Pitchers") +
  geom_point(data = YakBallsFramedR, aes(x = PlateLocSide, y = PlateLocHeight, color = CatcherStance, text = paste("Pitch Type: ", TaggedPitchType, "<br>Batter Side: ", BatterSide, "<br> Pitcher Set:", PitcherSet))) +
  scale_color_manual(breaks = c("R" ,"L", "P"), values = c("gold", "black", "dark grey")) +
  coord_cartesian(xlim = c(-4.5, 4.5), ylim = c(0, 5.5)) +
  theme(axis.line = element_blank(),
      axis.text.x = element_blank(),
      axis.text.y = element_blank(),
      axis.ticks = element_blank(),
      axis.title.x = element_blank(),
      axis.title.y = element_blank(),
      panel.background = element_blank(),
      panel.border = element_blank(),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      plot.background = element_blank(),
      legend.title = element_blank())
## Warning: Ignoring unknown aesthetics: text
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
layout(ggplotly(YakGraphR, tootip = "text"), dragmode = "pan")